Skip to content

COMMONSXML-7: SchemaFactory: capability-driven hardening - #13

Merged
garydgregory merged 3 commits into
apache:mainfrom
ppkarwasz:feature/schema-feature-based-hardening
Jul 5, 2026
Merged

COMMONSXML-7: SchemaFactory: capability-driven hardening#13
garydgregory merged 3 commits into
apache:mainfrom
ppkarwasz:feature/schema-feature-based-hardening

Conversation

@ppkarwasz

Copy link
Copy Markdown
Member

Applies the COMMONSXML-1 capability-driven approach to the Schema path, replacing the per-implementation class-name dispatch for SchemaFactory with a single wrapper-based recipe shared by every implementation.

  • HardeningSchemaFactory installs a deny-all LSResourceResolver on the factory (compile-time xs:import/include/redefine) and rewrites every newSchema(Source[]) through an XmlFactories-hardened reader.
  • HardeningSchema re-installs the resolver on each Validator/ValidatorHandler (validate-time xsi:schemaLocation), and HardeningValidator rewrites the Source on every validate(Source).
  • No FSP and no limit configuration on the factory: the hardened reader from XmlFactories.harden(Source) already carries them, so DOCTYPE / external-entity / Billion Laughs payloads in the schema or instance document are bounded there.
  • StockJdkProvider.configure(SchemaFactory), XercesProvider.configure(SchemaFactory) with its per-product hardeners, XmlFactories.dispatch(SchemaFactory) and Limits.applyToJdkSchema are removed; XmlFactories.newSchemaFactory() wraps in HardeningSchemaFactory directly. No new class is introduced (net -84 lines).

Behaviour: the JAXP 1.5 ACCESS_EXTERNAL_* properties are deliberately not set (the deny-all resolver covers the same fetches, and dropping them avoids the JDK 8 property-override bug, letting a caller re-enable specific lookups via their own resolver). The JDK block now surfaces as SecurityException rather than SAXException; the attack-test assertions already accept both. An unrecognized implementation is hardened rather than rejected. The schema-engine-internal maxOccurs node limit is no longer pinned on the factory (not exercised by any test); document-parsing limits remain covered via the hardened reader. mvn verify green across the stock JDK and external Xerces profiles.

Replace the per-implementation class-name dispatch for Schema with a single
wrapper-based recipe, the same for every implementation. The hardening lives
entirely in the existing HardeningSchemaFactory / HardeningSchema /
HardeningValidator wrappers, so there is no per-implementation branching, no
FEATURE_SECURE_PROCESSING and no limit configuration on the factory itself:

- HardeningSchemaFactory installs a deny-all LSResourceResolver on the factory
  (blocking xs:import/include/redefine at compile time) and rewrites every
  newSchema(Source[]) through an XmlFactories-hardened reader.
- HardeningSchema wraps every Validator/ValidatorHandler the inner Schema
  produces and re-installs the deny-all resolver on each (blocking
  xsi:schemaLocation at validation time), since neither the JDK nor Xerces
  reliably propagates it through Schema.
- HardeningValidator rewrites the Source on every validate(Source) call.

The hardened reader from XmlFactories.harden(Source) already carries
FEATURE_SECURE_PROCESSING and the processing limits, so a DOCTYPE, external
entity or Billion Laughs payload in the schema or instance document is bounded
there rather than on this factory.

The JAXP 1.5 ACCESS_EXTERNAL_* properties are deliberately not set: the deny-all
resolver already blocks the same fetches on every implementation, and the JDK 8
SchemaFactory has a bug whereby those properties keep blocking even when a
caller's own resolver would grant the access, so leaving them unset lets a
caller re-enable specific lookups by swapping the resolver. The JDK block now
surfaces as the resolver's SecurityException rather than a SAXException, which
the attack-test assertions already accept.

StockJdkProvider.configure(SchemaFactory), XercesProvider.configure(SchemaFactory)
with its per-product hardeners, XmlFactories.dispatch(SchemaFactory) and
Limits.applyToJdkSchema are removed; XmlFactories.newSchemaFactory() wraps in
HardeningSchemaFactory directly. An implementation is no longer rejected for
being unrecognized.

Assisted-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@garydgregory

Copy link
Copy Markdown
Member

The Jira ticket is COMMONSXML-7

@garydgregory

Copy link
Copy Markdown
Member

Hi @ppkarwasz
This PR has conflicts. Would you please rebase & resolve? TY!

@ppkarwasz

ppkarwasz commented Jul 5, 2026

Copy link
Copy Markdown
Member Author

I merged it with main to resolve conflicts and reordered the methods according to the rules.

@garydgregory
garydgregory merged commit 3738224 into apache:main Jul 5, 2026
15 checks passed
@garydgregory

Copy link
Copy Markdown
Member

Thank you @ppkarwasz , merged 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants